_TEXT	segment byte public 'CODE'
_TEXT	ends
DGROUP	group	_DATA,_BSS
	assume	cs:_TEXT,ds:DGROUP
_DATA	segment word public 'DATA'
d@	label	byte
d@w	label	word
_DATA	ends
_BSS	segment word public 'BSS'
b@	label	byte
b@w	label	word
_BSS	ends

include SegsAndOrgs.inc
; SEG_F000  segment at 0F000h 
;         org  0f80dh
;         old_expand_target label byte
; SEG_F000 ends
;
; SEG_CALL_WORKER  segment at 04000h
;         org 0
;         offset_call_worker label byte
; SEG_CALL_WORKER ends
;
; SEG_0000  segment at 0000h
;         org 07c00h               ; location unaltered Int19 handler would have jumped to
;         int19_handler label byte
; SEG_0000 ends
        
;#########################################
;
;           P U B L I C S
;
;#########################################

Public returning_to_original_bios, expand, unhide_worker_code, phase11, continue_boot_os 
Public write_near_call, voodoo_LAR, setup_voodoo_segs, voodoo_SGDT, voodoo_LGDT_spinallentries
Public voodoo_LGDT, voodoo_load_desc_in_ds, phase3, we_have_voodoo, old_bios_expansion
ifdef  COMPRESSWORKER
   Public HDR, dcmpstart, setup_for_term_scan, notready$1, readsuccess$1, setup_for_dcmp
   Public ProcessWithChecks, ReadLoop, term_string_found, look_for_termination_string, MinFound
   Public noreadblock$1, bitloop, onebit, readsuccess$2, notready$2, noreadblock$2
   Public nowriteblock$1, zerobit, nowriteblock$2, ready, fail, jProcessWithChecks
endif  ; COMPRESSWORKER

;#########################################
;
;           E Q U A T E S
;
;#########################################

LGDT_SS_SI EQU dd 014010f36h    ; 36 0f 01 14  lgdt qword ptr ss:[si]

vd_desc struc
lmt   dw  0
bsl   dw  0
bsm   db  0
typel db  0
typeh db  0
bsh   db  0
vd_desc ENDS

ifdef COMPRESSWORKER
include  cmphdr.inc

cmpdata segment USE16 para public 'cmpdata'
HDR             CMPRESS_HEADER   <>
cmpdata ends 

endif  ; COMPRESSWORKER

_TEXT	segment byte public 'CODE'
   ;	
.386   
	assume	cs:_TEXT
        

;this code provides the hooking trickery for p4b266_1007 bios
        
;this is a multi phase hooking process

;the idea is to run a worker application just prior to the boot strap loader(int19).  We were only
;able to run something during Int 19.

; if built with COMPRESSWORKER defined it will decompress the worker application before branching 
;    to it  

;phase 1) all this code is placed at f000:e560  (3e560 on the eeprom chip) 
;    note: the makefile will build a binary image of the bios eeprom chip
;          this binary image will be complete and it can be programmed on the
;          the eeprom chip and insterted into the motherboard
;    note: the only hex editor I've found that can handle this size of a bios image is the editor 
;       built in to the device programer app(BP mycrosystems)
;2) the bios expansion code(f000:e1f6 or 3e1f6 on the bios eeprom chip) was altered with a hex editor
;       to jump to f000:e560. At the time of expansion bios had copied seg f000 to seg 2000 and it jumps 
;       from 2000:e1f6 to us at 2000:e560
phase2_offset   equ     0e560h   ;that will be at the f000:e560 or 3e560 on the bios chip
;3) copy worker code from e000:a440(2a440 on chip) on eeprom chip to 2M region for a while.
;4) normal bios expansion is performed, using our copy of the expansion code in this file
;    note; after expansion, the bios is located in system memory and is read only, the eeprom chip 
;       is hidden, the branch to the boot strap loader is made valid during the expansion
;5) the bootstrap loader(int 19) handler at f000:1DB6 is altered to jump to phase 9 hook code
;    note: earlier attempts to locate worker code in 640K of normal real mode memory failed
;       because the bios wipes everything clean several times.
;    note: attempts to branch pre int 19 fail because 40:0 Bios data area is not filled in and
;        extended 1nt13 services are not available yet.
;6) phases 9 thru 13 hook code is copied to a hole at f000:3500 in system memory 
phase9_offset   equ     03500h
;8) bios is allowed to continue it's natural course for a long while
;9) phase 9 hook code comes alive during boot strap loader(int 19), or expanded code at
;       address f000:1db6 branches to f000:3500
;10) Move worker code.  If worker code compressed, move it from 2M region to seg 3000 and decompress
;    to seg 4000 else move worker code to seg 4000
;11) call the worker code
;       note: worker code will be responsible for clean up and branching to boot strap loader
;       at 0000:7c00
;12) clean off worker code
;13) banch back to int 19 handler


; the secret to bios hooking is to think of out port80 as chapters in a bios's life
;  by poking around and watching out 80's I found this bios does it's expansion at
;  out 80,c5 and it's branch to boot strap loader at out 80,ff


; we came here by hacking eeprom chip to branch from f000:e1f6 to f000:0000

;phase 2) the original bios branches here
;phase 9) the original bios branches here
expand: 
        jmp phase3
        db 06h dup (90h) ;a little buffer space 
        
; phase 10)  we've gotten here from int19(boot strap loader). Most of low memory should be wiped clean
;            and not in use. I had to find the right spot in int19 to branch here so int13x services
;            are available.
unhide_worker_code:  ; will be run at end of bios, should be located at f000:3500 at run time
        ; ds might have a voodoo seg value of f000 in it
        ; copy worker code we hid at 2M do seg 4000
        
        ;save old gdt because it seemed to effect int13x services
        sub  sp, 6   ; space for old gdt pointer
        mov  si, sp  ; pass pointer to store old gdt pointer
        call voodoo_SGDT
        call setup_voodoo_segs
        ;at this point ds and es should have voodoo seg that points at 8:0 or 80L
        mov  dx, ds
        call voodoo_LAR         ; get access rights of selector in DX
        cmp eax, 008f9300h      ; if we don't like the access rights bail out,
        jnz  continue_boot_os   ; ignore junk on the stack, it will be reinitialized during boot
        
        mov esi, 01fff80h     ; worker code hidden way up at 2M ds=80,1fff80+80=200000 or 2M
ifdef COMPRESSWORKER
        mov edi, 2ff80h       ; put compressed code at seg 3000 and decompress it to seg 4000
                              ; es=8 2ff80+80 = 30000, or 3000:0000 = 8:2ff80 at this point
else
        mov edi, 3ff80h       ; es=8 3ff80+80 = 40000, or 4000:0000 = 8:3ff80 at this point
endif
        mov ecx, 35e0h        ; that's how much space (in words) there is where it came from 2a440 thru 30fff
        rep movsw [edi], [esi]
        


ifdef COMPRESSWORKER
;DCO I found this decompression(and its compression counterpart) on a web site dedicated to this kind
;DCO   of stuff. It was a DR Dobbs winner and is a published open source technology

dcmpstart:	        ;DCO
                        ;DCO point ds to compressed data
			mov	ax, 3000h            ;DCO
			mov	ds, ax               ;DCO       ; probably pop out of voodoo mode here for ds
	                assume  ds:cmpdata           ;DCO
                        ;DCO scan for special string jammed on end of compressed data to find end
                        mov     es, ax               ;DCO       ; probably pop out of voodoo mode here for ds
                        xor     edi, edi             ;DCO
setup_for_term_scan:    ;DCO
                        add     di, HDR.CmpBufferStrt ; DCO they had the readbuffer(compressed), 
                                                      ; writebuffer(decompressed), 
                                                      ; these(ex., CmpBufferStrt) vars kind of scattered 
                                                      ; in the ds seg. The data placed in the readbuffer 
                                                      ; was read from STDIN. The decompressed data was 
                                                      ; written to STDOUT. I moved the writebuffer to 
                                                      ; its own seg, put all the vars in a struct, cat 
                                                      ; that struct to the top of the compressed 
                                                      ; data, cat a signature string on the end of the 
                                                      ; compressed data so I could find the end of the 
                                                      ; compressed data, put the data on the bios chip,
                                                      ; and pulled the STDOUT-IN stuff. This organization 
                                                      ; (varstruct/compresseddata/sig) allowed the 
                                                      ; least alteration of the actual decompression
                                                      ; algorithm and provide an adaptation that fit a
                                                      ; bios decompression scheme.  
                        mov     ecx, 3fffh ;DCO 64k dwords to scan
                        mov     eax, edi   ;DCO reduce ecx for size of header
                        shr     eax, 2     ;DCO
                        sub     ecx, eax   ;DCO
                        mov     eax, 'trpc' ;DCO

look_for_termination_string:  ;DCO
                        repnz   scasd   ;DCO
                        scasd           ;DCO
                        jz      term_string_found    ;DCO
                        ;DCO didn't find term string yet, align to next byte and try again
                        and     edi, 0003            ;DCO
                        inc     di                   ;DCO
                        jmp     setup_for_term_scan  ;DCO
term_string_found:      ;DCO
                        sub     di, 8      ;DCO di should now contain a pointer that points at the end of the compressed data
                        ;DCO put decompressed data at seg 4000 and 64k scribble space at seg 5000
                        mov     ax, di                  ;DCO
                        mov     HDR.CmpBufferEnd, ax    ;DCO
                        mov     ax, 0ffffh              ;DCO
                        mov     HDR.WriteBufferEnd, ax  ;DCO might as well right down the size of the compressed data now
                        mov     ax, 04000h              ;DCO
                        mov     fs, ax                  ;DCO
                        mov     ax, 05000h              ;DCO
                        mov     es, ax                  ;DCO
                        ;DCO found that scribble space needs all 20's in it
                        mov     cx, 8000h               ;DCO
                        mov     ax, 2020h               ;DCO
                        xor     di, di                  ;DCO
                        rep     stosw                   ;DCO

; si contains pointer to ReadBuffer    ;DCO this is an original writers comment
;DCO I changed the name ReadBuffer to CmpBufferStrt 
;DCO their above comment is a lie, they use a false CmpEnd and 
;DCO   an accurate CmpBufferEnd to determine when to fill/read the read buffer from STDIN
;   I'm skipping the read stuff because my adaptation is to have the compressed data in memory
			mov	si, offset HDR.CmpBufferEnd 
; di contains pointer to WriteBuffer   ;DCO this is an original writers comment 
;DCO the WriteBuffer is where the expanded code will end up
;DCO    es should have a scribble space segment 
;DCO    fs should be decompressed data
;DCO    ds should be compressed data
;DCO    si is used for a pointer in compressed data
;DCO    bx is used for a pointer in a scribble space
;DCO    di is used as a pointer in the decompressed data
                        ;mov	di, offset WriteBuffer ;DCO moved it the same seg as the 
                        ;                                   compressed data
                        xor     di, di   ;DCO I want it to start at the  beginning of seg
; bx contains the Addr          ;DCO this is an original writers comment 
			xor	bx, bx
                        mov     HDR.CmpEnd, si  ;DCO point it somewhere for now, use to signal 
                                                ; first time through
ReadLoop:		mov	ax, HDR.CmpEnd
			sub	ax, si
			xor	dx, dx
			div	HDR.nine
			cmp	ax, 1
			jbe	jProcessWithChecks
			mov	dx, ax
			dec	dx

			;mov	ax, offset WriteBufferEnd ;DCO moved it to the same seg as the
                        ;                                  DCO   compressed data in a struct
                        mov     ax, HDR.WriteBufferEnd    ;DCO
			sub	ax, di
			mov	cl, 3
			shr	ax, cl
			cmp	ax, 1
			jbe	jProcessWithChecks
			dec	ax
			mov	cx, ax

			cmp	cx, dx
			jbe	MinFound
			mov	cx, dx
MinFound:

ByteLoop:		lodsb
			mov	dl, al


			CALL ProcessBit                    ;DCO they had a macro I went for  
			CALL ProcessBit                    ;DCO space vs speed 
			CALL ProcessBit                    ;DCO and changed to procs
			CALL ProcessBit                    ;DCO
			CALL ProcessBit                    ;DCO
			CALL ProcessBit                    ;DCO
			CALL ProcessBit                    ;DCO
			CALL ProcessBit                    ;DCO 

			loop	jByteLoop
			jmp	ReadLoop
jByteLoop:	jmp	ByteLoop
ProcessWithChecks:
;DCO: fake it out like we just read in the data
		cmp	si, HDR.CmpEnd
		jb	noreadblock$1
		;push	bx                                ;DCO  our adaptation
		;mov	dx, offset ReadBuffer             ;DCO  doesn't read
		;mov	si, dx                            ;DCO  from STDIN
		;mov	bx, InFile                        ;DCO
		;mov	ah, 3Fh                           ;DCO
		;mov	cx, ReadBufferSize                ;DCO
		;int	21h                               ;DCO
		;pop	bx                                ;DCO
                ;jnc	readsuccess$1                     ;DCO
		;jmp	fail                              ;DCO
                mov     ax, HDR.CmpBufferEnd
                cmp     ax, HDR.CmpEnd      ; DCO made unequal to trigger no longer first time through
                jnz     setup_for_dcmp
                ;   we're done     ;DCO this is an original writers comment 
                xor     ax, ax 
                jmp     readsuccess$1
setup_for_dcmp: mov     HDR.CmpEnd, ax         ;DCO mark it not first time through anymore
                sub     ax, HDR.CmpBufferStrt  ;DCO make ax look like read size
                mov     si, HDR.CmpBufferStrt  ;DCO
readsuccess$1:
		or	ax, ax
		jne	notready$1
		jmp	ready
notready$1:
		add	ax, HDR.CmpBufferStrt   ;DCO struct used
		mov	HDR.CmpEnd, ax          ;DCO struct used
noreadblock$1:
		lodsb
;
		mov	dl, al
		mov	cx, 8
bitloop:
		shl	dl, 1
		jnc	zerobit
onebit:
; read a byte   ;DCO this is an original writers comment 
;DCO moved compressed data from STDIN to a seg
		cmp	si, HDR.CmpEnd                ;DCO  struct used
		jb	noreadblock$2
		;push	bx                            ;DCO   our addaptation
		;push	cx                            ;DCO   doesn't do 
		;push	dx                            ;DCO   STDIN any more
		;mov	dx, offset ReadBuffer         ;DCO 
		;mov	si, dx                        ;DCO 
		;mov	bx, InFile                    ;DCO 
		;mov	ah, 3Fh                       ;DCO 
		;mov	cx, ReadBufferSize            ;DCO 
                mov     cx, HDR.CmpEnd
		;int	21h                           ;DCO 
		;pop	dx                            ;DCO 
		;pop	cx                            ;DCO 
		;pop	bx                            ;DCO 
		;jnc	readsuccess$2                 ;DCO 
		;jmp	fail                          ;DCO 
                
                jmp     ready
readsuccess$2:
		or	ax, ax
		jne	notready$2
		jmp	ready
notready$2:
		add	ax, HDR.CmpBufferStrt         ;DCO struct used
		mov	HDR.CmpEnd, ax                ;DCO struct used
noreadblock$2:
		lodsb
;
		mov	es:[bx], al     ;DCO they keep track of stuff in scribble seg
		mov	bh, bl
		mov	bl, al
; Write a byte          ;DCO an original writers comment
                cmp     di, HDR.WriteBufferEnd     ;DCO moved to struct
		;jb	nowriteblock$1
		jnz	nowriteblock$1   ;DCO have we exhaused the write buffer, our condition a little dif
		;call	WriteBlock       ;DCO no longer writing to STDOUT
                jmp     fail
nowriteblock$1:
		;mov	ds:[di], al    ;DCO moved it to a seperate seg
		mov	fs:[di], al    ;DCO write out decompressed byte
		inc	di             ;DCO they bump pointer to decompressed data here
		loop	bitloop
		jmp	ReadLoop

zerobit:
		mov	al, es:[bx]     ;DCO they are stashing something in scribble seg 
		mov	bh, bl
		mov	bl, al
; Write a byte
		;cmp	di, offset WriteBufferEnd  ;DCO moved it to the compressed data seg
                cmp     di, HDR.WriteBufferEnd
		jb	nowriteblock$2
		;call	WriteBlock      ;DCO don't write to stdout anymore
                jmp     fail
nowriteblock$2: ;DCO this block seems redundant with nowriteblock$1, but I'm altering the
                ;     compression algorithm 
		;mov	ds:[di], al   ;DCO: moved it to a seperate seg
		mov	fs:[di], al   ;DCO: write out decompressed byte
		inc	di            ;DCO they bump pointer to decompressed data here 
		loop	bitloop
		jmp	ReadLoop

ready:	        ;call	FlushBuffer   ;DCO this is our newly planned exit point

                ;DCO: clear the scribble space out
                mov     cx, 8000h       ;DCO
                xor     ax, ax          ;DCO
                xor     di, di          ;DCO
                rep     stosw           ;DCO
                ;DCO: clear compressed data out
                mov     ax, ds          ;DCO
                mov     es, ax          ;DCO
                mov     cx, 8000h       ;DCO
                xor     ax, ax          ;DCO
                xor     di, di          ;DCO
                rep     stosw           ;DCO
                
		;mov	ax, 4c00h     ;DCO removed dos exit
		;int	21h           ;DCO
                jmp phase11             ; sucessful exit
fail:
                ;hlt
        ;bail out and branch to where we came from in int19 vector        
        db 0eah
        db 00h
        db 7ch
        db 00h
        db 00h
        
		;mov	dx, offset HDR.FailString  ;DCO  different failure path and
		;mov	ah, 9                      ;DCO  not using DOSexit
		;int	21h                        ;DCO
		;mov	ax, 4c01h                  ;DCO
		;int	21h                        ;DCO

jProcessWithChecks:
	jmp	ProcessWithChecks

ProcessBit	proc near                          ;DCO they had this a maco, I went for space saving
			shl	dl, 1
			jnc	zerobitp
			lodsb
			mov	es:[bx], al   ;DCO: stashing some stuff in scribble seg
			jmp	short over
zerobitp:		mov	al, es:[bx]
over:		        mov	bh, bl
			mov	bl, al
			;mov	ds:[di], al   ;DCO: moved it to a seperate seg
			mov	fs:[di], al   ;DCO: write out decompressed byte
			inc	di
                        ret
ProcessBit	endp

endif  ; COMPRESSWORKER
        
       
phase11:        ; phase 11)  call worker code
        ;restore old gdt because it seemed to effect int13x services
        ;mov ax, 295ah  ; the gdt they were using
        ;call ax
        mov  si, sp     ;the copy we saved
        call voodoo_LGDT
        add sp, 6   ; de-allocate the old gdt pointer
        mov dx, 10h  ;this particular descriptor helps things work
        call voodoo_load_desc_in_ds
                      
        call far ptr SEG_CALL_WORKER:offset_call_worker

; phase 12) clean off worker code
        mov ax, SEG_CALL_WORKER                ; 4000h
        mov es, ax
        xor ax, ax
        xor di, di
        mov cx, 8000h           ; a segment's worth of words
        rep stosw
        
continue_boot_os:       ; phase 13) branch back to int19
        ; branch to boot strap loader, where we interupted it
        ; it will then call_ScanDisk via jump pounded into int 19 handler

        jmp far ptr SEG_0000:int19_handler

        
;routine writes a near call where you tell it. 
;es:di is where to write near call
;si    is offset of routine to be called is same seg(ds points there at this point)
; registers preserved, flags not preserved
write_near_call      PROC    near
        push cx
        mov  cx, di
        add  cx, 3  ;offset of near call starts at next instruction
        not  cx     ; 2's completent of call spot
        inc  cx
        add  cx, si ; cx now contains +- offset from called base
        mov  byte ptr es:[di], 0e8h ; 
        mov  word ptr es:[di]+1, cx
        pop  cx
        ret
write_near_call      ENDP

;routine returns LAR in ax of seg in dx
voodoo_LAR     PROC    near
        push ecx
        push ebx
        mov eax, cr0
        or  al, 1
        mov cr0, eax
        xor ebx,ebx
        mov bx, dx
        LAR ecx, ebx
        mov eax, cr0
        and al, 0feh
        mov cr0, eax
        mov eax, ecx
        pop ebx
        pop ecx
        ret
voodoo_LAR     ENDP


DESC struc
lmt   dw  0
bsl   dw  0
bsm   db  0
typel db  0
typeh db  0
bsh   db  0
DESC ENDS

; this routine is a little complex because it is relocatabe to just
; about anywhere and it will put you in voodoo mode when called
; it will return with ds=es=8 and the base for 8 will be 80L
; The "call here_now, pop si, add si..." locates the gdt "dummy".
setup_voodoo_segs     PROC    near
        push eax
        push esi
        xor eax, eax
        xor esi, esi
        call here_now   ;we need our current ip to perform calculations
    here_now:   
        pop si          ; si should now have ip for here_now label
        add si, offset dummy - offset here_now  ;calculate address of dummy
        jmp over_gdt
        ;gdt32   dw  20h,dummy,000fh    going to build this on the stack on the fly
        dummy    DESC  <>                               ;seg 0
        sdata1   DESC  <0ffffh,0080h,0h,93h,8fh>        ;seg 8
        sdata2   DESC  <0ffffh,0100h,0h,93h,8fh>        ;seg 10 machine wouldn't boot with out this one
    over_gdt:
        ; push the limit and flat pointer for gdt on the stack
        mov ax, cs
        shl eax, 4
        add eax, esi
        push eax    ;flat pointer for dummy
        push 0018h  ; 3 8byte entries at dummy 
        mov si, sp
        ;can't get instruction to assemble so we patched 
        ;lgdt fword ptr cs:[si]  ;db 2eh ;db 0fh ;db 01h ;db 14h
        ;lgdt fword ptr ss:[si]
        LGDT_SS_SI
;       db 36h
;       db 0fh
;       db 01h
;       db 014h
        mov eax, cr0  ; switch to protect mode
        or  al, 1
        mov cr0, eax
        mov ax, 0008h ;load up selectors
        mov es, ax
        mov ds, ax
        mov eax, cr0  ; go back to real mode
        and al, 0feh
        mov cr0, eax
        pop eax  ;clear gdt pointer and limit off stack
        pop ax
        pop esi
        pop eax
        ret
setup_voodoo_segs     ENDP

; ss:si will contain place to store current gdt contents
voodoo_SGDT     PROC    near
        sgdt fword ptr ss:[si]
        ret
voodoo_SGDT     ENDP

; ss:si will contain place to reload gdt contents from
voodoo_LGDT_spinallentries     PROC    near
        push eax
        push ebx
        push ecx
        ;lgdt fword ptr ss:[si]
        LGDT_SS_SI
;       db 36h
;       db 0fh
;       db 01h
;       db 014h
        mov eax, cr0
        or al, 01
        mov cr0, eax
        ; reload the entries, hope we don't trap
        mov  ax, word ptr ss:[si]
        mov  si, ax
        shr  si, 3   ; we now know how many entries there are in the gdt
        xor  bx, bx
   check_num_entries:     
        or si, si
        jz back_to_real
        mov ds, bx
        mov es, bx
        dec si
        add bx, 8
        jmp check_num_entries
   back_to_real:          
        mov eax, cr0
        and al, 0FEh
        mov cr0, eax
        pop ecx
        pop ebx
        pop eax
        ret
voodoo_LGDT_spinallentries     ENDP


; ss:si will contain place to reload gdt contents from
voodoo_LGDT     PROC    near
        ;lgdt fword ptr ss:[si]
        LGDT_SS_SI
;       db 36h
;       db 0fh
;       db 01h
;       db 014h
        ret
voodoo_LGDT     ENDP

; dx will contain descriptor
; routine will check access rights before loading 
voodoo_load_desc_in_ds     PROC    near
        push eax
        call voodoo_LAR
        cmp  eax, 008f9300h
        jne  desc_no_good
        mov  eax, cr0
        or   al, 01
        mov  cr0, eax
        mov  ds, dx
        mov  eax, cr0
        and  al, 0FEh
        mov  cr0, eax
        pop  eax
        ret
  desc_no_good:

voodoo_load_desc_in_ds     ENDP


phase3:
        ; at this point it so happens that ds:5000 and es:2000. These are not
        ;   normal segs. They are voodoo segs, but I don't trust them 
        ;   I will take action to insure we have ds and es voodoo segs
        push ds
        push es
        push edi
        push esi
        push eax
        push edx
        push ecx
        push ebx
        

        ; when original bios does out 80, c5 we are at 2000:e1f6(the f000 seg was copied to 
        ; 2000 and we are running there at this time. It can be found on
        ; eeprom at 3e1f6 or f000:e1f6. This is the spot where the original bios expands and 
        ; is prime for before and after expansion trickery
        ; I put a copy of the original expansion code at the label "old_bios_expansion"
        
        call setup_voodoo_segs
        ;at this point ds and es should have voodoo seg that points at 8:0 or 80L
        ; we could test for voodoo and if it fails we could copy a special version of hook code 
        ; that goes up at 2000:d000
        mov  dx, ds
        call voodoo_LAR
        cmp  eax, 008f9300h
        jz   we_have_voodoo
           ;copy return opcodes from end of phase 9 to just after bios expansion
           mov ax, 2000h
           mov es, ax
           mov ds, ax
           mov di, bail_out_spot
           add di, phase2_offset
           mov si, returning_to_original_bios  
           add si, phase2_offset
           mov cx, end_of_return_to_original_bios_code
           sub cx, returning_to_original_bios 
           rep movsb
           jmp old_bios_expansion
we_have_voodoo:
        ; move worker code from e000:a440-f000:0fff to voodo memory, it will 
        ;    be near impossible to get to f000:0000-0ffff after bios expansion
        ;    not too hard to get to e000:a440 to e000:ffff after bios expansion, but 
        ;    there is space to work with to f000:0fff.  f0fff = f000:0fff or 30fff on chip
        mov esi, seg_offset_worker_source  ;our worker code is at ea440
        sub esi, 0080h       ; base of our voodo seg is at 80
        mov edi, 200000h  ; put it at 2M
        sub edi, 0080h    ; base of our voodo seg is at 80
        mov ecx, 35e0h    ; f1000 - ea44o = 35e0 is how many words there are from ea440 THRU f0fff
        rep movsw  [esi], [edi]
        
; phase 4)  bios expansion is performed
old_bios_expansion:        
       ;put context back to the way it was before we took over, about to allow bios to expand
        pop  ebx
        pop  ecx
        pop  edx
        pop  eax
        pop  esi
        pop  edi
        pop  es
        pop  ds

        
        ; perform bios decompression as it was
        ; $+2 is "here" + the size of the dw it appears in
        ; offset of this image     offset of first instruction
call_68cf: ; in this segment        in this image
        db 0e8h  ;      |                    |
        dw 068cfh - ( phase2_offset + ($+2 - expand))     ; displacement from here to 68cf in this seg
call_e208:
        db 0e8h
        dw 0e208h - ( phase2_offset + ($+2 - expand))     ; displacement from here to e208 in this seg

         mov al,0
      ; db 0b0h
      ; db 00h

call_71a0:
        db 0e8h
        dw 071a0h - ( phase2_offset + ($+2 - expand))     ; displacement from here to 71a0 in this seg

bail_out_spot:
        db 08h dup (90h) ;a little space to stuff branch back to bios if we should choose to abort 

        ;stash context again before we take over again        
        push ds
        push es
        push edi
        push esi
        push eax
        push edx
        push ecx
        push ebx
; phase 5)  hook into int19 handler
        ; need to set ram at f000 seg writeable to do that
        mov  eax, 80000090h  ;refer to intel 
        mov  dx, 0cf8h
        out  dx, eax
        add  dx, 04h
        in   al, dx
        mov  ah, al
        or   al, 20h       ;set WE bit
        mov  bx, ax             ; save setting in bx ###### warning, preserve bx 
        mov  eax, 80000090h
        mov  dx, 0cf8h
        out  dx, eax
        add  dx, 04h
        mov  ax, bx             
        out  dx, al
        ; write a jmp f000:3500 in int19 handler
        ; need it at f000:1db6, es happens to have a voodoo f000 in it 
        ; also this spot had a far jump so it's easy to recover or return where it left off
        mov di , 1db6h      
        mov byte ptr es:[di], 0eah
        mov word ptr es:[di] + 1, phase9_offset
        mov word ptr es:[di] + 3, 0f000h
; phase 6)
        ; move rest of hook code to from eeprom chip to seg f000
        mov si, 2000h   ;hate to move away from voodoo seg but we have it's way easier at this point
                        ;  and I've found that the pop ds at the end of this code restores
                        ;  the voodoo segs
        mov ds, si 
        mov si, offset expand + phase2_offset
        mov di, phase9_offset
        mov cx, offset phase3                ; calculate amount to copy 
        ;sub cx, offset expand                 ; used to be something another offset, expand is at 0 so we 
                                              ;   don't need thise line anymore  
        rep movsb
        ; put a jump to unhide_worker_code at f000:phase9_spot just like we did in phase3
        mov  di, phase9_offset
        mov  si, offset unhide_worker_code
        add  si, di 
        call write_near_call    ; really want a jump
                                ; would need to rewrite this proc to use voodoo segs
        mov  byte ptr es:[di], 0e9h ; opcode for near jmp with a 16 bit delta

        ; set f000 ram back to read only
        mov  eax, 80000090h
        mov  dx, 0cf8h
        out  dx, eax
        add  dx, 04h
        mov  ax, bx             ; restore PAM setting
        mov  al, ah             
        out  dx, al

        pop  ebx
        pop  ecx
        pop  edx
        pop  eax
        pop  esi
        pop  edi
        pop  es
        pop  ds
        
returning_to_original_bios:
; phase 8) let the original bios do its' job for a while
        ; by going where the old expand code went when it was done
        
        jmp  far ptr SEG_F000:old_expand_target         ; perform same jump the bios did

end_of_return_to_original_bios_code:

        db   "0.8"                      ; version
        ; put checksum fix up here
        db "dcopcsh"    ; this signature string is used to find this spot and insert a checksum
                        ; fixup during the build process
        db 00h
        db 00h
        db 00h
        
        
        
_TEXT	ends
_DATA	segment word public 'DATA'
_DATA	ends
_TEXT	segment byte public 'CODE'
_TEXT	ends
	end
